Skip to content

Fix hosted/vendored cargo floor reads (#653, #651) - #1429

Open
Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
mainfrom
agent/fix-cargo-declared-floor
Open

Mikola Lysenko (mikolalysenko) wants to merge 7 commits into
mainfrom
agent/fix-cargo-declared-floor

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #653
Fixes #651

What it does

Root cause

The project's declared cargo floor was read by one private helper in
vendor/cargo.rs, and only vendored mode called it. The helper took
[package].rust-version with .get(), so the { workspace = true }
table blocked the [workspace.package] fallback (#651). Hosted mode
never asked for the floor at all (#653).

The fix is one pure reader, formats/cargo/floor.rs, that both modes
call. It reads the floor the way cargo and rustup do:

  • the root rust-version, with .workspace = true inheritance;
  • for hosted, also the lowest floor among the member manifests the planner
    pins, because one cargo builds the whole lock;
  • else the toolchain rustup picks: the legacy rust-toolchain file over
    rust-toolchain.toml when both exist (rustup's own order);
  • a dated nightly-/beta-YYYY-MM-DD channel maps to its release train
    (stable ships every 6 weeks; 1.68 shipped on 2023-03-09).

rust-toolchain.toml and rust-toolchain are new read-only
(HOSTED | PRESENCE_ONLY) rows in formats/registry.rs. The hosted
planner sees them, but no rewriter edits them and they never trigger the
symlinked-read refusal. The contract and docs/ecosystems.md now give
the hosted cargo floor.

Not covered: toolchain files in ancestor directories, which rustup also
walks to. Hosted mode reads only the project root.

Tests

Red first, on the unchanged code (cargo test -p socket-patch-core --lib -- cargo_declared_floor_below_sparse_support_is_refused a_workspace_inherited_rust_version_is_the_declared_floor): 2 failed.
For example, the hosted test got left: [], right: ["redirect_cargo_old_cargo"]. Both are green after the fix.

Issue Regression test
#653 patch::redirect::tests::cargo_declared_floor_below_sparse_support_is_refused: rust-version 1.67 (also CRLF), an inherited 1.60, rust-toolchain.toml 1.67.1, rust-toolchain 1.56 and a member's 1.64 are each refused with nothing written. The test also checks the rollback hint over existing pins. 1.68 and an undeclared floor still redirect.
#653 e2e_redirect_cargo_shapes::cargo_hosted_refuses_a_project_declaring_cargo_below_1_68: the real CLI scan --mode hosted refuses, leaves the project byte-identical, and cargo fetch --locked still works.
#653 hosted::engine::tests::the_cargo_toolchain_files_are_read_as_advisory_input
#651 vendor::cargo::tests::a_workspace_inherited_rust_version_is_the_declared_floor: no multi-version warning for an inherited 1.70.
both formats::cargo::floor::tests::*: spellings, BOM, precedence, dated channels, member inheritance.

Local runs on e482907 (plus the blank-toolchain fix in bbd74db, re-tested with the floor and hosted unit tests):

  • cargo fmt --all -- --check: clean.
  • cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features (lib and every
    integration test): all pass except 4 permission tests (copy_tree
    symlinked root, vlt_heal unremovable lock, poetry/requirements
    write-failure). Those fail the same way on main because this sandbox
    runs as uid 0, which ignores chmod.
  • cargo test -p socket-patch-cli --all-features --lib --bins: 913
    passed.
  • The cargo e2e suites, with SOCKET_PATCH_CARGO_E2E_REQUIRED=1:
    e2e_redirect_cargo_build 22, e2e_redirect_cargo_shapes 31,
    e2e_vendor_cargo_build 39, mode_migration_cargo 29. All pass.
  • I couldn't build the full cargo test --workspace locally: its 150+ CLI
    test binaries need more disk than this session allows. CI runs the rest.

The npm, PyPI and gem wrappers have no cargo logic, so they needed no
change. /code-review high raised 7 findings. I fixed 6 in a follow-up
commit; the 7th (ancestor toolchain files) is out of scope, as noted above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Pr4cZSSSuEsv1rzzGoxTpg


Note

Medium Risk
Changes hosted Cargo redirect eligibility and shared floor parsing affect when pins are written; mistakes could block valid projects or still allow broken sparse pins, but behavior is fail-closed with tests and docs.

Overview
Introduces a shared formats/cargo/floor reader for the cargo version a project declares (rust-version, workspace inheritance, root rust-toolchain / rust-toolchain.toml), and wires it into both vendored and hosted Cargo paths.

Hosted (scan --mode hosted) now refuses sparse-registry redirects when that floor is below cargo 1.68, emitting redirect_cargo_old_cargo with no lock/manifest writes (sparse registries need 1.68+). Existing pins get a socket-patch rollback hint in the message. Hosted discovery reads toolchain files as advisory input only.

Vendored reuses the same floor logic so rust-version.workspace = true correctly inherits [workspace.package], fixing false cargo_multi_version_old_cargo warnings on modern workspaces.

Contract docs (CLI_CONTRACT.md, docs/ecosystems.md) and e2e/unit tests cover the new refusal and inheritance behavior.

Reviewed by Cursor Bugbot for commit 45b4799. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Hosted cargo patches are served from a sparse registry, which cargo
only loads from 1.68. A project that declared an older cargo in
rust-version or rust-toolchain[.toml] got "success" from scan
--mode hosted, and every build then failed with "usage of sparse
registries requires -Z sparse-registry". Hosted scan now refuses each
cargo patch there with redirect_cargo_old_cargo, writes nothing, and
points at --mode vendored (#653).

The declared floor now has one reader in formats/cargo, shared with
the vendored multi-version warning. It also follows
rust-version.workspace = true to [workspace.package], so a standard
modern workspace no longer gets a false "declares no rust-version"
warning when two versions of a crate are vendored (#651).

Assisted-by: Claude Code:claude-opus-5-5
Adds an e2e shape where rust-version = "1.67" makes scan --mode
hosted refuse the cargo patch and leave the project untouched, and an
engine test that the toolchain files reach the planner.

Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for the hosted cargo floor:
- the legacy rust-toolchain file wins over rust-toolchain.toml when
  both exist, as it does for rustup
- a dated nightly-/beta-YYYY-MM-DD channel maps to its release train,
  so a nightly from before sparse registries is refused too
- a workspace member's rust-version counts, since one cargo builds
  the whole lock
- the refusal names the rollback that drops pins an earlier run wrote
- vendored reads the toolchain files only when Cargo.toml says nothing
- the hosted candidate-file list test knows the toolchain files

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko Mikola Lysenko (mikolalysenko) changed the title Fix cargo floor reading in hosted/vendored (#653, #651) Fix hosted/vendored cargo floor reads (#653, #651) Oct 12, 2026
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 12, 2026 12:37
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/formats/cargo/floor.rs
The hosted engine records an unreadable in-memory rust-toolchain as
an empty file. Because rustup prefers the legacy file, that blank
placeholder hid rust-toolchain.toml's channel, and a project pinned
below cargo 1.68 was still redirected. A blank legacy file now names
no toolchain, so the TOML file's channel is used.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review (burn-down agent)

  • Head: bbd74db6989dcc3d4c9e19aa4d217349763d403d
  • CI: 35/35 green, 19 path-skipped; no main-wide failures.
  • Mergeable: yes, no conflicts with main.
  • Bugbot: reviewed bbd74db, no new issues. Its one earlier finding was fixed and the thread is resolved.
  • For reviewers: hosted mode now refuses Cargo patches when the project declares cargo < 1.68 (redirect_cargo_old_cargo), and rust-version.workspace = true now reads [workspace.package]. The shared floor reader is in formats/cargo/floor.rs.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review: needs a decision before this can be briefed

The question: when a workspace pins its toolchain, should a member's low rust-version still block the hosted cargo redirect?

Where: declared_floor at redirect/mod.rs:1750-1767.`` It takes .min() over two things together:

  • the root floor, which includes rust-toolchain(.toml)
  • every other manifest read (workspace members and in-root path dependencies, via member_manifests_in)

Example: root rust-version = "1.80", rust-toolchain.toml channel 1.82, and member b/Cargo.toml with rust-version = "1.56". Member b doesn't even depend on serde.

  • I reproduced this with a scratch test on bbd74db: serde is refused with redirect_cargo_old_cargo ("this project declares cargo 1.56") and nothing is written.
  • rustup builds that whole tree with 1.82, so this redirect works today and is refused after this PR.
  • This layout is common: library members publish a low MSRV while the workspace pins a modern toolchain.
  • The only way out is --mode vendored.

Options:

  1. A concrete toolchain pin decides when present. Fall back to the root's rust-version, then to the members' lowest floor, only when nothing higher up is declared.
  2. Keep min(), but count only real workspace members, not non-member path dependencies.
  3. Keep the current policy (refuse conservatively), but name the manifest the floor came from in the warning. Today it says only "rust-version or rust-toolchain", which is hard to act on in a large workspace.

I lean towards 1, since the PR's own reasoning is about the one cargo that builds the lock. It's a product call, though, so I'm leaving the code as is.

Non-blocking notes:

  • The rollback hint suggests pkg:cargo/<name>@<version being patched now>. If an earlier run pinned a different version, that purl won't match the existing pins.
  • In vendored mode, a legacy rust-toolchain now wins over rust-toolchain.toml. That matches rustup, but the description doesn't call out the vendored behavior change.

Otherwise checked:

  • The floor arithmetic in floor.rs is correct.
  • The tests fail on main.
  • CRLF and BOM are covered.
  • CHANGELOG.md is untouched.
  • CI: ci-ok and clippy are green, 54/54 check runs are passing or skipped, and Bugbot passed.

Labelled agent:needs-human.


Generated by Claude Code

Main's #1420 added a restored field to the e2e Shape struct; the
declared-cargo-floor test this branch adds predates it and no longer
compiled after merging main.

Co-Authored-By: Claude <noreply@anthropic.com>
@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Merged main (7236190) to clear the conflict; head is now 45b4799.

  • docs/ecosystems.md: kept both sides on the Cargo row: this PR's cargo 1.68+ / redirect_cargo_old_cargo clause and main's cargo vendor / local-registry refusal from Fix Cargo source replacement being ignored (#338, #455) #1420.
  • e2e_redirect_cargo_shapes.rs: Fix Cargo source replacement being ignored (#338, #455) #1420 added a restored field to Shape, so I added restored: vec[] to this PR's new 1.67 refusal shape.
  • Local: cargo check --all-targets for core and cli passes, and core's cargo lib tests pass (464). Ready for review is off until CI is green on the new head. The agent:needs-human question above is still open.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 45b4799. Configure here.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment